Ruby memory bloat
If your app is suffering from high memory usage, it’s best to investigate memory bloat first given it’s an easier problem to solve than a leak.
Ruby メモリ memory
https://www.bokukoko.info/entry/2016/10/18/211427
https://techracho.bpsinc.jp/hachi8833/2017_12_28/50109
https://techlife.cookpad.com/entry/2019/07/10/083318
https://jp.quora.com/プロダクションの-Rails-サーバーの利用メモリがひたすら
What causes Ruby memory bloat?
Rubyのmemory allocationの基礎
Rubyはオブジェクトを管理するためにRuby heap pageを持つ
40 bytesのスロットに分割され、1スロットに1オブジェクトを格納する
足りない場合はポインタを持つ
空きスロットがないときはヒープページを割り当てる
C、OSのmemory allocator
glibcのmalloc(size)によって割り当てられる
memory allocatorはカーネルAPIを通してメモリを割り当てるが、一度に4KB単位でしか割り当てられない
呼び出し元が要求するよりも多くのメモリを確保してしまうので余る。この余りがなくなるまで割り当て続ける
カーネル
4KB単位でしか割り当てられない。この単位をページという(OSページ)
パフォーマンス影響が大きいので極力呼び出しを減らしたい
topやpsでメモリ使用量を確認すると、カーネル観点でのメモリ使用量が表示される
実際には使われていない領域も含まれる
断片化
Rubyレベル
RubyのGCではRuybヒープページのスロットを空きとしてマークし、再利用できるようにする
ヒープページすべてが空きスロットになったらmemory allocatorを介して解放することができる
スロットが少しでも空いていない場合は解放できず、カーネルから見れば使用中
メモリアロケーターレベル
OSページに対しても同じことが起きうる
すべてが空いていなければ解放できず、1ページの空き領域で不足する場合は新たなページ割り当てを要求する
どちらかの理由でRuby memory bloatが起きる
マルチスレッドにおける過剰割り当て
複数のスレッドで同じOSヒープを扱うと競合するので、memory allocatorは複数のOSヒープを作成して各スレッドに割り当てる
このOSヒープ数がvCPU数の8倍
memory allocatorはRedHatが商用サーバ向けを想定して開発している
MALLOC_ARENA_MAX=2環境変数を設定することでスレッド単位のOSヒープ最大数を指定できる
malloc_trim
OSページを解放するC関数
これをGC中に呼べば断片化を解消できる?
ohbarye.icon https://bugs.ruby-lang.org/issues/15667 によると断片化は解消されずメモリ使用量が減るのみぽい
ohbarye.icon Ruby 3.3.0時点ではProcess.warmupにて使われるのみ
jemallocにより改善するケースも知られているが、versionによる差異もあるのでRubyのデフォルトにできるほど安定していない